using DG.Tweening;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

namespace BF
{

    public static class NetTimeUtil
    {
        /// <summary>
        /// 是否已获取到网络时间
        /// </summary>
        public static bool isInited { get; private set; }
        /// <summary>
        /// 是否使用本地时间
        /// </summary>
        public static bool isUseLocalTime { get; private set; }


        private static DateTime _netTime;
        private static DateTime _netTimeLocal;
        private static long _timestamp;
        private static float _realtimeSinceGetNetTime;


        static NetTimeUtil()
        {
            _netTime = DateUtil.DefaultUTC;
            _timestamp = DateUtil.DefaultUTC.GetTimestamp();
            _realtimeSinceGetNetTime = Time.realtimeSinceStartup;
            isUseLocalTime = false;

#if DEBUG
            if (PlayerPrefs.HasKey("NetTimeUtil_UseLocalTime"))
            {
                isUseLocalTime = PlayerPrefs.GetInt("NetTimeUtil_UseLocalTime") == 1;
            }
#endif
        }

#if DEBUG
        public static void SetUseLocalTime(bool isUseLocalTime)
        {
            NetTimeUtil.isUseLocalTime = isUseLocalTime;
            PlayerPrefs.SetInt("NetTimeUtil_UseLocalTime", isUseLocalTime ? 1 : 0);
        }
#endif

        public static void FixLocalTimeZone(float hourOffset)
        {
            _netTimeLocal = new DateTime(_netTime.Ticks, DateTimeKind.Local).AddHours(hourOffset);
        }


        public static void SetNetTime(long timestamp)
        {
            _timestamp = timestamp;
            _netTime = DateUtil.ToDateTime(timestamp);
            _netTimeLocal = _netTime.ToLocalTime();
            _realtimeSinceGetNetTime = Time.realtimeSinceStartup;
            isInited = true;
        }

        /// <summary>
        /// 拉取网络时间
        /// </summary>
        /// <param name="urls">访问的网络地址</param>
        /// <param name="onFinish">参数为是否成功拉取到网络时间</param>
        /// <param name="isForce"></param>
        public static void FetchNetTime(Action<bool> onFinish, IList<string> urls = null, bool isForce = false)
        {
            if (isForce)
            {
                isInited = false;
            }

            if (isInited)
            {
                return;
            }

            if (urls == null || urls.Count == 0)
            {
                urls = new string[] {
                    "http://www.google.com",
                    "http://www.apple.com",
                    "http://www.amazon.com",
                    "http://www.microsoft.com",
                    "https://www.baidu.com"
                };
            }


            int failedCount = 0;

            void OnFetchFinish(bool isSuccess)
            {
                if (!isSuccess)
                {
                    ++failedCount;
                    if (failedCount == urls.Count)
                    {
                        onFinish?.Invoke(false);
                        onFinish = null;
                        isInited = true;
                    }
                }
                else
                {
                    onFinish?.Invoke(true);
                    onFinish = null;
                }
            };
            foreach (string url in urls)
            {
                DoFetch(url, OnFetchFinish);
            }
        }

        private static void DoFetch(string url, Action<bool> onFinish)
        {
            UnityWebRequest request = UnityWebRequest.Get(url);
            request.timeout = 20;
            UnityWebRequestAsyncOperation async = request.SendWebRequest();
            async.completed += (temp) => {
                if (!isInited)
                {
                    if (!string.IsNullOrEmpty(request.error))
                    {
                        onFinish?.Invoke(false);
                    }
                    else if (request.GetResponseHeaders() == null)
                    {
                        onFinish?.Invoke(false);
                    }
                    else
                    {
                        string gmt = request.GetResponseHeader("Date");

                        if (string.IsNullOrEmpty(gmt))
                        {
                            onFinish?.Invoke(false);
                        }
                        else
                        {
                            if (DateTime.TryParse(gmt, out DateTime dtTemp))
                            {
                                _netTime = TimeZoneInfo.ConvertTimeToUtc(dtTemp);
                                _timestamp = _netTime.GetTimestamp();
                                _netTimeLocal = _netTime.ToLocalTime();
                                _realtimeSinceGetNetTime = Time.realtimeSinceStartup;
                                onFinish?.Invoke(true);
                                isInited = true;

                                Debug.Log($"[NetTimeUtil] url:{url}, gmt:{gmt}, time:{_netTime}");
                            }
                            else
                            {
                                onFinish?.Invoke(false);
                            }
                        }
                    }
                }
                request.Dispose();
            };
        }

        /// <summary>
        /// 当前时间
        /// </summary>
        public static DateTime Now
        {
            get
            {
                if (isUseLocalTime)
                {
                    return DateTime.Now;
                }
                return _netTimeLocal.AddSeconds(Time.realtimeSinceStartup - _realtimeSinceGetNetTime);
            }
        }

        /// <summary>
        /// 当前时间
        /// </summary>
        public static DateTime UTCNow
        {
            get
            {
                if (isUseLocalTime)
                {
                    return DateTime.UtcNow;
                }
                return _netTime.AddSeconds(Time.realtimeSinceStartup - _realtimeSinceGetNetTime);
            }
        }

        /// <summary>
        /// 当前时间戳
        /// </summary>
        public static long timestamp
        {
            get
            {
                if (isUseLocalTime)
                {
                    return DateTime.Now.GetTimestamp();
                }
                return _timestamp + Convert.ToInt64(Time.realtimeSinceStartup - _realtimeSinceGetNetTime);
            }
        }

        public static string DebugStr()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("[BF.NetTimeUtil]");
            sb.AppendLine($"Base->   timestamp:{_timestamp}, netTime:{_netTime}, realtimeSinceStartup:{_realtimeSinceGetNetTime}");
            sb.AppendLine($"Now->    timestamp:{timestamp}, netTime:{Now}, realtimeSinceStartup:{Time.realtimeSinceStartup}");
            sb.AppendLine($"Offset-> timestamp:{timestamp - _timestamp}, netTime:{(Now - _netTime).TotalSeconds}, realtimeSinceStartup:{Time.realtimeSinceStartup - _realtimeSinceGetNetTime}");
            return sb.ToString();
        }
    }
}
